package com.fulihui.information.biz.content.impl;

import com.fulihui.information.biz.content.FullDateManager;
import com.fulihui.information.core.db.ContentCounterDB;
import com.fulihui.information.dal.dataobj.InfoContent;
import com.fulihui.information.dal.dataobj.InfoSource;
import com.fulihui.information.dal.dataobj.InfoSourceExample;
import com.fulihui.information.dal.mapper.InfoSourceMapper;
import com.fulihui.information.dto.ContentListItemDTO;
import com.fulihui.information.model.ContentAuxiliaryData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.RecursiveTask;
import java.util.stream.Collectors;

import static java.util.Collections.emptyList;
import static org.springframework.util.CollectionUtils.isEmpty;

/**
 * @author Willard.Hu on 2016/12/28 0028.
 */
@Component
public class FullDateManagerImpl implements FullDateManager {
    @Autowired
    private InfoSourceMapper infoSourceMapper;
    @Autowired
    @Qualifier("redisContentCounterDB")
    private ContentCounterDB contentCounterDB;

    @Override
    public void fullSingle(ContentListItemDTO item) {
        if (item == null) {
            return;
        }
        InfoSource infoSource = infoSourceMapper.selectByPrimaryKey(item.getSourceCode());
        item.setSourceName(infoSource != null ? infoSource.getName() : null);
        long readTimes = contentCounterDB.singleTimes(ContentCounterDB.CounterType.READ_TIMES, item.getId());
        if (readTimes > 0) {
            item.setReadTimes(readTimes);
        }
        long upvote = contentCounterDB.singleTimes(ContentCounterDB.CounterType.UPVOTE, item.getId());
        if (upvote > 0) {
            item.setUpvote(upvote);
        }
        long downvote = contentCounterDB.singleTimes(ContentCounterDB.CounterType.DOWNVOTE, item.getId());
        if (downvote > 0) {
            item.setDownvote(downvote);
        }
        long shard = contentCounterDB.singleTimes(ContentCounterDB.CounterType.SHARED_TIMES, item.getId());
        if (shard > 0) {
            item.setShared(shard);
        }
        long commentTimes = contentCounterDB.singleTimes(ContentCounterDB.CounterType.COMMENT_TIMES, item.getId());
        if (commentTimes > 0) {
            item.setCommentTimes(commentTimes);
        }
        long starTimes = contentCounterDB.singleTimes(ContentCounterDB.CounterType.STAR_TIMES, item.getId());
        if (starTimes > 0) {
            item.setStarTimes(starTimes);
        }
    }

    @Override
    public <T extends ContentListItemDTO> void fullMulti(List<T> list) {
        ContentAuxiliaryData data = queryAuxiliaryData(list);
        if (data == null) {
            return;
        }
        int i = 0;
        for (ContentListItemDTO item : list) {
            if (!isEmpty(data.getSourceList())) {
                Optional<InfoSource> optional = data.getSourceList().stream()
                        .filter(it -> it.getCode().equals(item.getSourceCode()))
                        .findFirst();
                optional.ifPresent(infoSource -> item.setSourceName(infoSource.getName()));
                // 顺序与查询顺序一致
                if (!isEmpty(data.getReadTimesList())) {
                    Long readTimes = data.getReadTimesList().get(i);
                    if (readTimes != null && readTimes > 0) {
                        item.setReadTimes(readTimes);
                    }
                }
                // 顺序与查询顺序一致
                if (!isEmpty(data.getUpvoteList())) {
                    Long upvote = data.getUpvoteList().get(i);
                    if (upvote != null && upvote > 0) {
                        item.setUpvote(upvote);
                    }
                }
                // 顺序与查询顺序一致
                if (!isEmpty(data.getDownvoteList())) {
                    Long downvote = data.getDownvoteList().get(i);
                    if (downvote != null && downvote > 0) {
                        item.setDownvote(downvote);
                    }
                }
                // 顺序与查询顺序一致
                if (!isEmpty(data.getSharedTimesList())) {
                    Long shared = data.getSharedTimesList().get(i);
                    if (shared != null && shared > 0) {
                        item.setShared(shared);
                    }
                }
                // 顺序与查询顺序一致
                if (!isEmpty(data.getCommentTimesList())) {
                    Long commentTimes = data.getCommentTimesList().get(i);
                    if (commentTimes != null && commentTimes > 0) {
                        item.setCommentTimes(commentTimes);
                    }
                }
                // 顺序与查询顺序一致
                if (!isEmpty(data.getStarTimesList())) {
                    Long starTiems = data.getStarTimesList().get(i);
                    if (starTiems != null && starTiems > 0) {
                        item.setStarTimes(starTiems);
                    }
                }
                i++;
            }
        }
    }

    /**
     * 获取辅助数据，可补充领域模型中非表字段的属性
     * @param contentList {@link InfoContent} 列表
     * @return {@link ContentAuxiliaryData} 资讯内容辅助数据
     */
    @SuppressWarnings("unchecked")
    private <T extends ContentListItemDTO> ContentAuxiliaryData queryAuxiliaryData(List<T> contentList) {
        if (isEmpty(contentList)) {
            return null;
        }
        int taskSize = 4;
        List<RecursiveTask> tasks = new ArrayList<>(taskSize);
        RecursiveTask task;
        // Fork
        task = new FindSourceNameListTask(contentList);
        task.fork();
        tasks.add(task);
        task = new FindReadTimesListTask(contentList);
        task.fork();
        tasks.add(task);
        task = new FindSharedTimesListTask(contentList);
        task.fork();
        tasks.add(task);
        task = new FindStarTimesListTask(contentList);
        task.fork();
        tasks.add(task);
        task = new FindCommentTimesListTask(contentList);
        task.fork();
        tasks.add(task);
        task = new FindVoteupListTask(contentList);
        task.fork();
        tasks.add(task);
        // Join
        ContentAuxiliaryData data = new ContentAuxiliaryData();
        for (RecursiveTask t : tasks) {
            if (t instanceof FindSourceNameListTask) {
                data.setSourceList((List<InfoSource>) t.join());
            } else if (t instanceof FindReadTimesListTask) {
                data.setReadTimesList((List<Long>) t.join());
            } else if (t instanceof FindSharedTimesListTask) {
                data.setSharedTimesList((List<Long>) t.join());
            } else if (t instanceof FindStarTimesListTask) {
                data.setStarTimesList((List<Long>) t.join());
            } else if (t instanceof FindCommentTimesListTask) {
                data.setCommentTimesList((List<Long>) t.join());
            } else if (t instanceof FindVoteupListTask) {
                data.setUpvoteList((List<Long>) t.join());
            }
        }
        return data;
    }

    /**
     * 获取资讯来源名称列表
     */
    private class FindSourceNameListTask<T extends ContentListItemDTO> extends RecursiveTask<List<InfoSource>> {
        private static final long serialVersionUID = -4492998873293155130L;
        List<T> contentList;

        FindSourceNameListTask(List<T> contentList) {
            this.contentList = contentList;
        }

        @Override
        protected List<InfoSource> compute() {
            InfoSourceExample example = new InfoSourceExample();
            Set<String> unionSourceCode = contentList.stream()
                    .map(T::getSourceCode)
                    .collect(Collectors.toSet());
            example.createCriteria().andCodeIn(new ArrayList<>(unionSourceCode));
            return infoSourceMapper.selectByExample(example);
        }
    }

    /**
     * 获取资讯阅读数列表
     */
    private class FindReadTimesListTask<T extends ContentListItemDTO> extends RecursiveTask<List<Long>> {
        private static final long serialVersionUID = 5399633240496702520L;
        List<T> contentList;

        FindReadTimesListTask(List<T> contentList) {
            this.contentList = contentList;
        }

        @Override
        protected List<Long> compute() {
            if (isEmpty(contentList)) {
                return emptyList();
            }
            List<String> ids = contentList.stream()
                    .map(T::getId)
                    .collect(Collectors.toList());
            return contentCounterDB.multiTimes(ContentCounterDB.CounterType.READ_TIMES, ids);
        }
    }

    /**
     * 获取资讯分享数列表
     */
    private class FindSharedTimesListTask<T extends ContentListItemDTO> extends RecursiveTask<List<Long>> {
        private static final long serialVersionUID = 9146871804095554189L;
        List<T> contentList;

        FindSharedTimesListTask(List<T> contentList) {
            this.contentList = contentList;
        }

        @Override
        protected List<Long> compute() {
            if (isEmpty(contentList)) {
                return emptyList();
            }
            List<String> ids = contentList.stream()
                    .map(T::getId)
                    .collect(Collectors.toList());
            return contentCounterDB.multiTimes(ContentCounterDB.CounterType.SHARED_TIMES, ids);
        }
    }

    /**
     * 获取资讯收藏数列表
     */
    private class FindStarTimesListTask<T extends ContentListItemDTO> extends RecursiveTask<List<Long>> {
        private static final long serialVersionUID = 9146871804095554189L;
        List<T> contentList;

        FindStarTimesListTask(List<T> contentList) {
            this.contentList = contentList;
        }

        @Override
        protected List<Long> compute() {
            if (isEmpty(contentList)) {
                return emptyList();
            }
            List<String> ids = contentList.stream()
                    .map(T::getId)
                    .collect(Collectors.toList());
            return contentCounterDB.multiTimes(ContentCounterDB.CounterType.STAR_TIMES, ids);
        }
    }

    /**
     * 获取资讯评论数列表
     */
    private class FindCommentTimesListTask<T extends ContentListItemDTO> extends RecursiveTask<List<Long>> {
        private static final long serialVersionUID = 9146871804095554189L;
        List<T> contentList;

        FindCommentTimesListTask(List<T> contentList) {
            this.contentList = contentList;
        }

        @Override
        protected List<Long> compute() {
            if (isEmpty(contentList)) {
                return emptyList();
            }
            List<String> ids = contentList.stream()
                    .map(T::getId)
                    .collect(Collectors.toList());
            return contentCounterDB.multiTimes(ContentCounterDB.CounterType.COMMENT_TIMES, ids);
        }
    }

    /**
     * 获取资讯赞数列表
     */
    private class FindVoteupListTask<T extends ContentListItemDTO> extends RecursiveTask<List<Long>> {
        private static final long serialVersionUID = 9146871804095554189L;
        List<T> contentList;

        FindVoteupListTask(List<T> contentList) {
            this.contentList = contentList;
        }

        @Override
        protected List<Long> compute() {
            if (isEmpty(contentList)) {
                return emptyList();
            }
            List<String> ids = contentList.stream()
                    .map(T::getId)
                    .collect(Collectors.toList());
            return contentCounterDB.multiTimes(ContentCounterDB.CounterType.UPVOTE, ids);
        }
    }

    /**
     * 获取资讯踩数列表
     */
    private class FindVotedownListTask<T extends ContentListItemDTO> extends RecursiveTask<List<Long>> {
        private static final long serialVersionUID = 9146871804095554189L;
        List<T> contentList;

        FindVotedownListTask(List<T> contentList) {
            this.contentList = contentList;
        }

        @Override
        protected List<Long> compute() {
            if (isEmpty(contentList)) {
                return emptyList();
            }
            List<String> ids = contentList.stream()
                    .map(T::getId)
                    .collect(Collectors.toList());
            return contentCounterDB.multiTimes(ContentCounterDB.CounterType.DOWNVOTE, ids);
        }
    }
}
